#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from contextlib import contextmanager, closing
from urllib.request import urlopen

"""
在 python 中,读写文件资源要特别注意,要正确关闭他们
正确关闭文件资源的一种方式是使用 try...finally:


try:
    f = open('/path/to/file', 'r')
    f.read()
finally:
    f.close()

# 每次使用 try finally 非常麻烦,可以简化为 with

with open('/path/to/file', 'r') as f:
    f.read()

#:并不是只有 open()函数返回的 fp 对象才能使用 with 一句,实际上,任何对象,只要正确实现了上下文
管理,就可以用 with

实现上下文管理是通过  __enter__ 和 __exit__ 这两个方法实现的

"""


class Query(object):

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print("begin")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print("error")
        else:
            print('end')

    def query(self):
        print('query info about %s' % self.name)


# 这样我们就可以把自己写的资源对象用于 with 语句

with Query('Bob') as q:
    q.query()

"""
with 具体工作流程:
__enter__()方法被执行
__enter__()方法返回的值,执行代码块
__exit__()方法被调用


with 真正强大的地方是可以处理异常

"""

"""
@contextmanager
编写 __enter__ 和 __exit__ 依旧很繁琐,因此 python标准库 contextlib 提供了更简单的写法

"""


# 把上面的代码改写如下:
class Query2(object):

    def __init__(self, name):
        self.name = name

    def query(self):
        print('query info about %s' % self.name)


@contextmanager
def create_query2(name):
    print('begin')
    q = Query2(name)
    yield q
    print('end')


"""
@contextmanager这个decorator接受一个generator,
用yield语句把with ... as var把变量输出出去，然后，with语句就可以正常地工作了：

"""
with create_query2('allencm') as q:
    q.query()


# 很多时候我们希望在某段代码前后自动执行特定代码,也可以通过@contextmanager 实现
@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)


with tag('h1'):
    print("hello world")
    print("哈哈哈哈哈哈")

"""
上述代码执行顺序是:
1.with 语句首先执行 yield 之前的语句,因此打印出<h1>
2.yield 调用会执行 with 语句内部的所有语句,因此打印出 hello world
3.最后执行 yield 之后的语句,打印出 </h1>
"""

# @closing
# 如果一个对象没有实现上下文,我们就不能把它用于 wiht 语句,这个时候可用 closing() 来把
# 该对象变为上下文对象
# @closing 也是经过@contextmanager包装过的 generator

with closing(urlopen('https://www.baidu.com')) as page:
    for line in page:
        print(line)

"""
@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

他的作用就是把任意对象变为上下文对象,并支持 with 语句
"""
